﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BigfootMVC.Helpers
{

    /// <summary>
    /// This class is a helper class to build a JSON string. It is very useful when generating script server side.
    /// </summary>
    public class JSObject
    {

        /// <summary>
        /// Specifies the type of value you are adding to the JSObject. When other the value is not escaped or quoted as you 
        /// would have to do for a string.Y ou can nest JSObjects by adding a property to it, and passing as the value the 
        /// ToString method call of another JSObject
        /// </summary>
        public enum ValueTypeEnum
        {
            String,
            Other
        }

        /// <summary>
        /// Factory method to create a JSObject
        /// </summary>
        public static JSObject Create()
        {
            return new JSObject();
        }


        private Dictionary<string, string> _data = new Dictionary<string, string>();

        /// <summary>
        /// Adds a property to the json object. It asumes that it is not a string so it ouputs the value directly. 
        /// if the value is empty then it oupts null as the value for the property. You can nest JSObjects by
        /// adding a property to it, and passing as the value the ToString method call of another JSObject
        /// </summary>
        /// <param name="name">Property name {name}: {value}</param>
        /// <param name="value">Property value</param>
        public JSObject Add(string name, string value)
        {
            if (value == string.Empty)
            {
                value = "null";
            }
            return Add(name, value, ValueTypeEnum.Other);
        }

        /// <summary>
        /// Adds a property to the json object. It asumes that it is not a string so it ouputs the value directly. 
        /// if the value is empty then it does not add it. You can nest JSObjects by
        /// adding a property to it, and passing as the value the ToString method call of another JSObject
        /// </summary>
        /// <param name="name">Property name {name}: {value}</param>
        /// <param name="value">Property value</param>
        public JSObject AddIfNotEmpty(string name, string value)
        {
            return value == string.Empty ? this 
                                         : Add(name, value, ValueTypeEnum.Other);
        }

        /// <summary>
        /// Adds a property to the json object. It asumes that it is not a string so it ouputs the value directly. 
        /// if the value is empty then it oupts null as the value for the property. You can nest JSObjects by
        /// adding a property to it, and passing as the value the ToString method call of another JSObject
        /// </summary>
        /// <param name="name">Property name {name}: {value}</param>
        /// <param name="value">Property value</param>
        public JSObject Add(string name, int value)
        {
            return Add(name, value.ToString(), ValueTypeEnum.Other);
        }

        /// <summary>
        /// Adds a property to the json object as a string so it properly escapes it and quotes it 
        /// </summary>
        /// <param name="name">Property name {name}: {value}</param>
        /// <param name="value">Property value string will be escaped and quoted</param>
        public JSObject AddString(string name, string value)
        {
            return Add(name, value, ValueTypeEnum.String);
        }

        /// <summary>
        /// Adds a property to the json object. If the valuetype is a string it is escaped and quoted. If it is not it ouputs the value directly. 
        /// if the value is empty then it oupts null as the value for the property
        /// </summary>
        /// <param name="name">Property name {name}: {value}</param>
        /// <param name="value">Property value. string will be escaped and quoted</param>
        /// <param name="ValueType">The type of value being added. If a string, it will be escaped and quoted</param>
        public JSObject Add(string name, string value, ValueTypeEnum ValueType)
        {
            if (ValueType == ValueTypeEnum.String) value = JSBuilder.GetString(value);
            Data.Add(name, value);
            return this;
        }

        /// <summary>
        /// This is the list of properties that will output
        /// </summary>
        public Dictionary<string, string> Data
        {
            get { return _data; }
        }

        /// <summary>
        /// Merges another JSObject into this one
        /// </summary>
        public JSObject Merge(JSObject obj)
        {
            foreach (var key in obj.Data.Keys)
            {
                _data.Add(key, obj.Data[key]);
            }
            return this;
        }

        /// <summary>
        /// Counts how many properties have been added to this json object
        /// </summary>
        public int Count()
        {
            return _data.Count;
        }

        public bool HasMember(string memberName)
        {
            return _data.ContainsKey(memberName);
        }

        public string GetValue(string memberName)
        {
            return Data.ContainsKey(memberName) ? Data[memberName] : "";
        }

        /// <summary>
        /// Determines if no data has been added to the object
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty()
        {
            return Count() == 0;
        }

        /// <summary>
        /// Properly oupts the JSON sorrounded by { } braces
        /// </summary>
        public override string ToString()
        {
            var sb = new StringBuilder();
            sb.Append(" { ");
            var first = true;
            foreach (var key in _data.Keys)
            {
                if (first == false)
                {
                    sb.Append(", ");
                }
                first = false;
                sb.AppendFormat("{0}: {1}", key, _data[key]);
            }
            sb.Append(" } ");
            return sb.ToString();
        }
    }
}
